/**
  @authoer: lss
  @date: 2020/12/29
  @note:
**/
package tool

import (
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"reflect"

	uuid "github.com/satori/go.uuid"
)

// encode struct to json(byte[])
func StructToJson(structModel interface{}) ([]byte, error) {
	json, err := json.Marshal(structModel)
	return json, err
}

// encode struct to json string
func StructToJsonStr(structModel interface{}) (string, error) {
	json, err := json.Marshal(structModel)
	return string(json), err
}

// decode json(byte[]) to map[string]interface{}
func JsonToMap(jsonData []byte) (mapData map[string]interface{}, err error) {
	err = json.Unmarshal(jsonData, &mapData)
	return mapData, err
}

//string md5
func MD5(str string) string {
	ctx := md5.New()
	ctx.Write([]byte(str))
	return hex.EncodeToString(ctx.Sum(nil))
}

//get new uid
func GetNewUuid() string {
	//some version uuid.NewV4() return 2 results
	return uuid.Must(uuid.NewV4(), nil).String()
}

func FileExist(path string) bool {
	_, err := os.Lstat(path)
	return !os.IsNotExist(err)
}

func WriteFile(filename string, data []byte, perm os.FileMode) error {
	f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
	if err != nil {
		return err
	}
	n, err := f.Write(data)
	if err == nil && n < len(data) {
		err = io.ErrShortWrite
	}
	if err1 := f.Close(); err == nil {
		err = err1
	}
	return err
}

func MapCopy(tags map[string]interface{}) map[string]interface{} {
	cloneTags := make(map[string]interface{})
	for k, v := range tags {
		cloneTags[k] = v
	}
	return cloneTags
}

func MapUpdate(dst, src map[string]interface{}) map[string]interface{} {
	return jsMerge(dst, src, 0)
}

func jsMerge(dst, src map[string]interface{}, depth int) map[string]interface{} {

	if depth > 32 {
		return dst
		// panic("too deep!")
	}

	for key, srcVal := range src {

		if dstVal, ok := dst[key]; ok {

			srcMap, srcMapOk := jsMapify(srcVal)
			dstMap, dstMapOk := jsMapify(dstVal)

			if srcMapOk && dstMapOk {
				srcVal = jsMerge(dstMap, srcMap, depth+1)
			}
		}

		dst[key] = srcVal
	}

	return dst
}

func jsMapify(i interface{}) (map[string]interface{}, bool) {

	value := reflect.ValueOf(i)

	if value.Kind() == reflect.Map {

		m := map[string]interface{}{}

		for _, k := range value.MapKeys() {
			m[k.String()] = value.MapIndex(k).Interface()
		}

		return m, true
	}

	return map[string]interface{}{}, false
}

func Strip(s_ string, chars_ string) string {
	s, chars := []rune(s_), []rune(chars_)
	length := len(s)
	max := len(s) - 1
	l, r := true, true //标记当左端或者右端找到正常字符后就停止继续寻找
	start, end := 0, max
	tmpEnd := 0
	charset := make(map[rune]bool) //创建字符集，也就是唯一的字符，方便后面判断是否存在
	for i := 0; i < len(chars); i++ {
		charset[chars[i]] = true
	}
	for i := 0; i < length; i++ {
		if _, exist := charset[s[i]]; l && !exist {
			start = i
			l = false
		}
		tmpEnd = max - i
		if _, exist := charset[s[tmpEnd]]; r && !exist {
			end = tmpEnd
			r = false
		}
		if !l && !r {
			break
		}
	}
	if l && r { // 如果左端和右端都没找到正常字符，那么表示该字符串没有正常字符
		return ""
	}
	return string(s[start : end+1])
}

func StructSliceToMapSlice(source interface{}) {
	// 判断，interface转为[]interface{}
	v := reflect.ValueOf(source)
	if v.Kind() != reflect.Slice {
		panic("ERROR: Unknown type, slice expected.")
	}
	l := v.Len()
	ret := make([]interface{}, l)
	for i := 0; i < l; i++ {
		ret[i] = v.Index(i).Interface()
	}

	// 转换之后的结果变量
	res := make([]map[string]interface{}, 0)

	// 通过遍历，每次迭代将struct转为map
	for _, elem := range ret {
		data := make(map[string]interface{})
		objT := reflect.TypeOf(elem)
		objV := reflect.ValueOf(elem)
		for i := 0; i < objT.NumField(); i++ {
			data[objT.Field(i).Name] = objV.Field(i).Interface()
		}
		res = append(res, data)
	}

	fmt.Printf("==== Convert Result ====\n%+v\n", res)
}

//把类似slice的map转为slice
func MapToSlice(param []map[string]interface{}, pluck string) []interface{} {
	var output []interface{}
	length := len(param)
	if length <= 0 {
		return output
	}
	for i, l := 0, len(param); i < l; i++ {
		output = append(output, param[i][pluck])
	}
	return output
}

func FormatRealNameStar(realName string) string {
	nameRune := []rune(realName)
	if len(nameRune) == 2 {
		return string(nameRune[:1]) + "*"
	}
	if len(nameRune) == 3 {
		return string(nameRune[:1]) + "*" + string(nameRune[len(nameRune)-1:])
	}
	if len(nameRune) > 3 {
		for i, r := range nameRune {
			if i%3 != 0 {
				if r != '·' {
					nameRune[i] = '*'
				}
			}
		}
	}
	return string(nameRune)
}
